/*
 * LibMediaDecoder.cpp
 *
 *  Created on: 2018年7月23日
 *      Author: terry
 */

#include "LibMediaDecoder.h"
#include "Application.h"
#include "LastError.h"
#include "Version.h"

static PlayerHolderPtr getPlayer(HANDLE handle)
{
	return Application::instance().get(handle);
}



/**
 * 初始化解码库
 * @return TRUE 如果成功
 */
DLLEXPORT BOOL WINAPI MDecode_Init()
{
	Application::instance().init();

#ifdef WIN32
	//
#else
	//MDecode_EnableHwAccel(true);
#endif //

	return true;
}

/**
 * 清理解码库
 */
DLLEXPORT void WINAPI MDecode_Uninit()
{
	Application::instance().quit();
}

DLLEXPORT const char* WINAPI MDecode_GetVersion()
{
	return APP_VERSION;
}

/**
 * 获取操作的错误码
 * @param handle 解码器句柄，NULL 表示全局错误码
 * @return 0 表示没有错误
 */
DLLEXPORT int WINAPI MDecode_GetLastError(HANDLE handle)
{
	return LastError::get();
}


/**
 * 获取解码器句柄
 * @return 解码器句柄， NULL表示失败， 需要调用MDecode_DestroyPlayer进行关闭
 */
DLLEXPORT HANDLE WINAPI MDecode_CreateHandle()
{
	PlayerHolderPtr player(new PlayerHolder());
	HANDLE handle = player.get();

	Application::instance().put(handle, player);

	return handle;
}

/**
 * 销毁解码器句柄
 * @param handle 解码器句柄
 */
DLLEXPORT void WINAPI MDecode_DestroyHandle(HANDLE handle)
{
	PlayerHolderPtr player = Application::instance().remove(handle);
	if (player)
	{
		player->close();
	}
	return ;
}


/**
 * 打开流式解码器
 * @param handle 解码器句柄
 * @param buffer 缓冲区指针
 * @param length 缓冲区长度
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_OpenStream(HANDLE handle, const MDecode_Format* fmt)
{
	if (!fmt)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	player->openStream(fmt);

	return true;
}


/**
 * 关闭流式解码器
 * @param handle 解码器句柄
 */
DLLEXPORT void WINAPI MDecode_CloseStream(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		return;
	}

	player->close();

	return ;
}


/**
 * 输入视频数据
 * @param handle 解码器句柄
 * @param buffer 数据缓冲区
 * @param length 数据长度
 * @param mediaType 数据类型，比如视频，音频，复合流
 * @return TRUE 表示解码成功
 */
DLLEXPORT BOOL WINAPI MDecode_InputData(HANDLE handle, int mediaType, const uint8_t* buffer, int length, int64_t pts, int flags)
{
	if (!buffer || length <= 0)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->inputData(mediaType, buffer, length, pts, flags);
}


/**
 * 重置输入缓冲区
 * @param handle
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_ResetBuffer(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	player->resetBuffer();

	return true;
}


/**
 * 设置显示窗口
 * @param handle 解码器句柄
 * @param hwnd  窗口句柄
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetVideoWnd(HANDLE handle, HWND hwnd)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->setVideoWnd(hwnd);
}



/**
 * 获取视频分辨率，调用MDecode_InputData解码成功之后才会返回有效值
 * @param pWidth 宽
 * @param pHeight 高
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_GetPictureSize(HANDLE handle, int* pWidth, int* pHeight)
{
	if (!pWidth || !pHeight)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->getPictureSize(*pWidth, *pHeight);
}

/**
 * 抓图，格式为BMP
 * @param handle 解码器句柄
 * @param filename 文件名
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Snap(HANDLE handle, const char* filename)
{
	if (!filename)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->snap(filename);
}

/**
 * 刷新窗口
 * @param handle 解码器句柄
 */
DLLEXPORT void WINAPI MDecode_Refresh(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		return;
	}

	player->refresh();
	return ;
}

/**
 * 播放
 * @param handle
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Play(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->play();
}

/**
 * 暂停
 * @param handle
 * @param pauseIt TRUE 表示暂停，FALSE表示恢复
 * @param TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Pause(HANDLE handle, BOOL pauseIt)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->pause(pauseIt > 0);
}

/**
 * 停止
 * @param handle 解码器句柄
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Stop(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	player->stop();

	return true;
}



/**
 * 设置解码回调
 * @param handle 解码句柄
 * @param cb 回调函数
 * @param pUser 环境指针
 *
 */
DLLEXPORT BOOL WINAPI MDecode_SetDecodeCallback(HANDLE handle, MDecode_PixelFormat pixelFormat, MediaDecodeCallback cb, void* pUser)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	player->setDecodeCallback(pixelFormat, cb, pUser);
	return true;
}

/**
 * 设置显示回调，可在回调中进行绘图操作
 * @param handle 解码器句柄
 * @param cb 回调函数
 * @param pUser 环境指针
 */
DLLEXPORT BOOL WINAPI MDecode_SetDrawCallback(HANDLE handle, MediaDrawCallback cb, void* pUser)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	player->setDrawCallback(cb, pUser);
	return true;
}




/**
 * 打开文件
 * @param filename 文件名
 * @return 解码器句柄， 需要调用MDecode_CloseFile关闭
 */
DLLEXPORT BOOL WINAPI MDecode_OpenFile(HANDLE handle, const char* filename)
{
	if (!filename)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->openFile(filename, "");
}


/**
 * 关闭文件解码器
 * @param handle 解码器句柄
 */
DLLEXPORT void WINAPI MDecode_CloseFile(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		return;
	}

	player->close();

	return ;
}


/**
 * 打开文件
 * @param url 文件名或者网络URL
 * @param params 参数, 可以为NULL或者空
 * @return 解码器句柄， 需要调用 MDecode_CloseFile 关闭
 */
DLLEXPORT BOOL WINAPI MDecode_OpenFileEx(HANDLE handle, const char* url, const char* params)
{
	if (!url)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->openFile(url, params);
}



/**
 * 获取播放进度
 * @param handle
 * @return 播放进度，范围为[0, 1]
 */
DLLEXPORT double WINAPI MDecode_GetPos(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return 0.0;
	}

	return player->getPos();
}

/**
 * 设置播放文件，即定位操作seek
 * @param handle 解码器句柄
 * @param pos 进度值，范围为[0, 1]
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetPos(HANDLE handle, double pos)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->setPos(pos);
}

/**
 * 获取文件的总时长,
 * @param handle 解码器句柄
 * @param pSeconds 总时长，单位为秒，有些文件无法获取时长
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_GetDuration(HANDLE handle, double* pSeconds)
{
	if (!pSeconds)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	*pSeconds = ((double)player->getDuration()) / 1000;
	return true;
}


/**
 * 设置播放速度
 * @param handle 解码器句柄
 * @param scale 播放速度，范围为[1/8, 8], 1表示正常速度，8 表示快八倍速，1/8表示慢八倍速
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetScale(HANDLE handle, double scale)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->setScale(scale);
}


/**
 * 获取播放速度
 * @param handle
 * @param pScale
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_GetScale(HANDLE handle, double* pScale)
{
	if (!pScale)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	*pScale = player->getScale();
	return true;
}


/**
 * 设置文件播放结束通知消息，消息的wparam表示句柄，lparam保留
 * @param handle 解码器句柄
 * @param hwnd 消息窗口句柄
 * @param msg 消息类型
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_SetFileEndMsg(HANDLE handle, HWND hwnd, uint32_t msg)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->setFileEndMsg(hwnd, msg);
}



/**
 * 打开音频解码
 * @param handle
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL  WINAPI MDecode_OpenSound(HANDLE  handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->openSound();
}

/**
 * 关闭音频解码
 * @param handle 解码器句柄
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL  WINAPI MDecode_CloseSound(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	player->closeSound();

	return true;
}

DLLEXPORT BOOL  WINAPI MDecode_IsSoundOpen(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	player->isSoundOpen();

	return true;
}

/**
 * 设置音频音量
 * @param handle 解码器句柄
 * @param volume 音量，范围为：[0-100], 0 表示静音
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL  WINAPI MDecode_SetVolume(HANDLE    handle, int  volume)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->setVolume(((float)volume) / 100);
}


/**
 * 获取音量
 * @param handle
 * @param pVolume 音量
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL  WINAPI MDecode_GetVolume(HANDLE    handle, int*  pVolume)
{
	if (!pVolume)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	float volume = player->getVolume();
	*pVolume = (int)(volume * 100);

	return true;
}



/**
 * 获取当前播放的时间
 * @handle 解码库句柄
 * @pts  时间，单位为毫秒
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_GetTime(HANDLE handle, int64_t* pts)
{
	if (!pts)
	{
		return LastError::set(EINVAL);
	}

	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	*pts = player->getTime();
	return true;
}

/**
 * 定位到指定时间点
 * @param handle	解码库句柄
 * @param pts	时间, 单位为毫秒
 * @return TRUE 表示成功
 */
DLLEXPORT BOOL WINAPI MDecode_Seek(HANDLE handle, int64_t pts)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->seek(pts);
}


/**
 * 设置显示比例
 * 如果cx,cy= 0, 保持视频的原始比率
 * @param player
 * @param cx	宽度比例
 * @param cy	高度比例
 * @return
 */
DLLEXPORT BOOL WINAPI MDecode_SetRatio(HANDLE handle, int cx, int cy)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->setRatio(cx, cy);
}



/**
 * 设置事件回调句柄
 * @param player	播放器
 * @param cb		回调函数
 * @param pUser		回调环境
 * @return
 */
DLLEXPORT BOOL MDecode_SetEventCallback(HANDLE handle, DecoderEventCallback cb, void* pUser)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->setEventCallback(cb, pUser);
}

DLLEXPORT BOOL MDecode_SetVideoFilter(HANDLE handle, const char* filterString)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->setVideoFilter(filterString);
}

DLLEXPORT void MDecode_EnableHwAccel(BOOL enabled)
{
	av::LitePlayerFactory::enableHwAccel(enabled > 0);
}

DLLEXPORT BOOL MDecode_StartRecord(HANDLE handle, const char* filepath)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	player->startRecord(filepath);
	return true;
}

/**
 * 停止录像
 * @param handle
 * @return
 */
DLLEXPORT BOOL MDecode_StopRecord(HANDLE handle)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	player->stopRecord();
	return true;
}

/**
 * 是否在录像中.
 * @param handle
 * @return TRUE 表示在录像.
 */
DLLEXPORT BOOL MDecode_IsRecording(HANDLE handle)
{
    PlayerHolderPtr player = getPlayer(handle);
    if (!player)
    {
        LastError::set(ENOENT);
        return false;
    }

	return player->isRecording();
}


static MediaWriterPtr getWriter(HANDLE handle)
{
	return Application::instance().getWriter(handle);
}




/**
* 创建录像
* @param filename 文件名
* @return 录像句柄， NULL表示失败
*/
DLLEXPORT HANDLE WINAPI MDecode_OpenRecord(const char* filename, const MDecode_Format* fmt)
{
	return MDecode_OpenRecordEx(filename, fmt, MDECODE_CODEC_NONE);
}


/**
* 写入媒体流
* @param handle 录像句柄
* @param buffer 数据指针
* @param length    数据长度
* @param mediaType  数据类型
* @return TRUE 表示成功
*/
DLLEXPORT BOOL WINAPI MDecode_WriteRecord(HANDLE handle, int mediaType,
	const uint8_t* buffer, int length, int64_t pts, int flags)
{
	if (!buffer || length <= 0)
	{
		return  false;
	}

	MediaWriterPtr writer = getWriter(handle);
	if (!writer)
	{
		return false;
	}

	if (mediaType == av::MEDIA_TYPE_AUDIO)
	{
		writer->writeAudio(const_cast<uint8_t*>(buffer), length, pts, flags);
	}
	else
	{
		writer->writeVideo(const_cast<uint8_t*>(buffer), length, pts, flags);
	}
	return 0;
}


/**
* 关闭录像
* @param handle 录像句柄
*
*/
DLLEXPORT void WINAPI MDecode_CloseRecord(HANDLE handle)
{
	MediaWriterPtr writer = Application::instance().removeWriter(handle);
	if (!writer)
	{
		return;
	}
	
	writer->close();
}

DLLEXPORT HANDLE WINAPI MDecode_OpenRecordEx(const char* filename, const MDecode_Format* fmt, int codec)
{
	if (!filename || !fmt)
	{
		return NULL;
	}

	MediaWriterPtr writer;
	av::MediaFileFormat fileFormat = av::FILE_AUTO;
	av::MediaWriterFactory::guessFileFormat(filename, fileFormat);

	writer.reset(av::MediaWriterFactory::createNormWriter(fileFormat), av::MediaWriterFactory::destroy);
	//if (codec == MDECODE_CODEC_PS)
	//{
	//	writer.reset(av::MediaWriterFactory::createPsInputWriter(fileFormat), av::MediaWriterFactory::destroy);
	//}
	//else
	//{
	//	writer.reset(av::MediaWriterFactory::create(fileFormat), av::MediaWriterFactory::destroy);
	//}
	
	if (!writer)
	{
		return NULL;
	}

	if (fileFormat == av::FILE_MP4)
	{
		writer->setOutputAudioCodec(AV_CODEC_ID_AAC);
	}

	MFormat format = MFormat();
	format.codec = fmt->codec;
	format.channels = fmt->channels;
	format.sampleRate = fmt->samplerate;
	format.audioCodec = fmt->audioCodec;
	format.vProp = fmt->prop;
	format.vPropSize = fmt->propSize;
	format.configSize = fmt->configSize;
	format.config = fmt->config;

	format.clockRate = 1000000;
	format.audioRate = 1000000;

	if (!writer->open(filename, format))
	{
		return NULL;
	}
	 
	HANDLE handle = writer.get();
	Application::instance().putWriter(handle, writer);

	return handle;
}

BOOL WINAPI MDecode_SetDelayTime(HANDLE handle, int milliseconds)
{
    PlayerHolderPtr player = getPlayer(handle);
    if (!player)
    {
        LastError::set(ENOENT);
        return false;
    }

    return player->setDelayTime(milliseconds);
}

DLLEXPORT BOOL WINAPI MDecode_SetAudioFormat(HANDLE handle, int codec, int channels, int samplerate, const uint8_t* config, int configSize)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return false;
	}

	return player->setAudioFormat(codec, channels, samplerate, config, configSize);
}

DLLEXPORT BOOL WINAPI MDecode_SetParam(HANDLE handle, const char* name, const char* value)
{
    PlayerHolderPtr player = getPlayer(handle);
    if (!player)
    {
        LastError::set(ENOENT);
        return false;
    }

    return player->setParam(name, value);
}

DLLEXPORT BOOL WINAPI MDecode_SetDecodeCallbackFreq(HANDLE handle, int freq)
{
    PlayerHolderPtr player = getPlayer(handle);
    if (!player)
    {
        LastError::set(ENOENT);
        return false;
    }
    return player->setDecodeCallbackFreq(freq);
}

DLLEXPORT void WINAPI MDecode_SetFaceTrackDuration(int ms)
{
	if (ms <= 0)
	{
		return;
	}

	PlayerHolder::FACE_TRACK_DURATION = 1000 * ms;
}

DLLEXPORT void WINAPI MDecode_EnableDrawGraph(HANDLE handle, int mode)
{
    PlayerHolderPtr player = getPlayer(handle);
    if (!player)
    {
        LastError::set(ENOENT);
        return ;
    }

    player->enableDrawGraph(mode);
}

DLLEXPORT void WINAPI MDecode_SetDrawGraph(HANDLE handle, const char* value, size_t size)
{
    PlayerHolderPtr player = getPlayer(handle);
    if (!player)
    {
        LastError::set(ENOENT);
        return ;
    }

    player->setDrawGraph(value, size);
}

DLLEXPORT const char* WINAPI MDecode_GetDrawGraph(HANDLE handle)
{
    PlayerHolderPtr player = getPlayer(handle);
    if (!player)
    {
        LastError::set(ENOENT);
        return "";
    }
    return player->getDrawGraph();
}

DLLEXPORT void WINAPI MDecode_RemoveDrawGraph(HANDLE handle, int mode)
{
	PlayerHolderPtr player = getPlayer(handle);
	if (!player)
	{
		LastError::set(ENOENT);
		return ;
	}
	return player->removeDrawGraph(mode);
}

DLLEXPORT void WINAPI MDecode_SetFullscreen(HANDLE handle, BOOL isFull)
{
    PlayerHolderPtr player = getPlayer(handle);
    if (!player)
    {
        LastError::set(ENOENT);
        return ;
    }
    player->setFullscreen(isFull > 0);
}

DLLEXPORT BOOL WINAPI MDecode_IsFullscreen(HANDLE handle)
{
    PlayerHolderPtr player = getPlayer(handle);
    if (!player)
    {
        LastError::set(ENOENT);
        return false;
    }
    return player->isFullscreen();
}
